#include <string.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>

#include <sys/time.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <assert.h>

#include <psp2/kernel/clib.h>
#include <psp2/ctrl.h>
#include <psp2/io/stat.h>
#include <psp2/touch.h>
#include <kubridge.h>

#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
#include <SDL2/SDL_mixer.h>
#include <SDL2/SDL_ttf.h>

#include "patch.h"

static SDL_Texture *SS2D_bg_texture_bak = NULL;

void *__wrap_memcpy(void *dest, const void *src, size_t n)
{
	return sceClibMemcpy(dest, src, n);
}

void *__wrap_memmove(void *dest, const void *src, size_t n)
{
	return sceClibMemmove(dest, src, n);
}

void *__wrap_memset(void *s, int c, size_t n)
{
	return sceClibMemset(s, c, n);
}

struct tm *localtime_soloader(time_t *timer)
{
	struct tm *res = localtime(timer);
	if (res)
		return res;
	// Fix an uninitialized variable bug.
	time(timer);
	return localtime(timer);
}

void nop_range(uintptr_t addr, size_t len) // From KAAAsS
{
	// 暂时只支持 thumb mode
	assert(addr & 0x1);
	addr &= ~0x1;
	void *buf = malloc(len);
	for (size_t i = 0; i < len; i += 2)
	{
		*(uint16_t *)(buf + i) = 0x46c0;
	}
	kuKernelCpuUnrestrictedMemcpy((void *)addr, buf, len);
	free(buf);
}

void RenderFullScreen(int bFullScreen)
{
	if (bFullScreen)
	{
		SS2D_render_dst_rect->w = SCREEN_W;
		SS2D_render_dst_rect->h = SCREEN_H;
	}
	else
	{
		SS2D_render_dst_rect->w = ((float)GAME_WIDTH * ((float)SCREEN_H / (float)GAME_HEIGHT));
		SS2D_render_dst_rect->w &= ~1;
		SS2D_render_dst_rect->h = SCREEN_H;
	}
	SS2D_render_dst_rect->x = (SCREEN_W - SS2D_render_dst_rect->w) / 2;
	SS2D_render_dst_rect->y = (SCREEN_H - SS2D_render_dst_rect->h) / 2;
	*SS2D_render_scale_x = (float)GAME_WIDTH / (float)SS2D_render_dst_rect->w;
	*SS2D_render_scale_y = (float)GAME_HEIGHT / (float)SS2D_render_dst_rect->h;

	debugPrintf("RenderCopy rect.x: %d, rect.y: %d, rect.w: %d, rect.h: %d\n", SS2D_render_dst_rect->x, SS2D_render_dst_rect->y, SS2D_render_dst_rect->w, SS2D_render_dst_rect->h);
	debugPrintf("RenderCopy scale_x: %f, scale_y: %f\n", *SS2D_render_scale_x, *SS2D_render_scale_y);

	if (bFullScreen)
		*SS2D_bg_texture = NULL;
	else
		*SS2D_bg_texture = SS2D_bg_texture_bak;
}

void rbtree_print(rbtree_node *node)
{
	if (!node)
		return;

	if (node->left)
		rbtree_print(node->left);

	printf("StringDBmap[%d]: %s\n", node->key, (char *)node->value);

	if (node->right)
		rbtree_print(node->right);
}

void printStringDB()
{
	rbtree_print(StringDBmap->root);
}

int DrawLogo(void)
{
	static int bVisited = 0;

	if (!bVisited)
	{
		bVisited = 1;

		stringDB_1624 = GetStringDB(1624);

		SS2D_bg_texture_bak = *SS2D_bg_texture;
		RenderFullScreen(setting_config.full_screen);
		// printStringDB();

		// Recreate main_texture from main_surface
		// (main_surface pixel format is r5g6b5, main_texture original pixel format is rgba8888,  do this for no need to convert pixels)
		// (Ori: main_surface ==> conversion_surface(rgba8888), conversion_surface->pixels ==> main_texture; New: main_surface->pixels ==> main_texture)
		SDL_Renderer *renderer = SDL_SS2D_GetRenderer((void *)SS2D_ADDRESS);
		if (*SS2D_main_texture)
			SDL_DestroyTexture(*SS2D_main_texture);
		*SS2D_main_texture = SDL_CreateTextureFromSurface(renderer, *SS2D_main_surface);
		debugPrintf("SS2D_renderer: %p, SS2D_main_surface: %p, SS2D_main_texture: %p\n", renderer, *SS2D_main_surface, *SS2D_main_texture);
	}

	return 0;
}

int initPatchMain()
{
	hook_addr(so_symbol(&so_mod, "_Z8DrawLogov"), (uintptr_t)&DrawLogo);

	return 0;
}

void patch_game()
{
	loadSettingConfig(&setting_config);

	initPatchMain();
	initPatchAndroid();
	initPatchUIGamePad();
	initPatchSdl();
	initPatchSdlSs2d();
	initPatchFile();
	initPatchFont();
	initPatchCommButtonClass();
	initPatchBinker();
	initPatchSetting();
}
